/**
 * 
 */
package wong.board.client.http;

import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONValue;

/**
 * <p>
 * The class <code>JSONRequest</code>
 * </p>
 * 
 * @author wong
 */
public class JSONRequest<V extends JSONValue> {

	private Request request;
	private Throwable error;
	private RequestHandler<V> handler;

	public JSONRequest() {

	}

	public void get(String url, Callback<V> callback) {
		RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url);
		try {
			handler = new RequestHandler<V>(callback);
			request = builder.sendRequest(null, handler);
		} catch (RequestException e) {
			callback.onError(e);
			request = null;
			error = e;
		}
	}

	public Throwable getError() {
		return error;
	}

	public void cancel() {
		if (request != null) {
			request.cancel();
			if (handler != null) {
				handler.callback.onCancel();
			}
			flush();
		}
	}

	protected void flush() {
		request = null;
	}

	public interface Callback<CallbackValue extends JSONValue> {
		void onReceive(CallbackValue value);

		void onError(Throwable error);

		void onCancel();
	}

	private class RequestHandler<RequestValue extends JSONValue> implements RequestCallback {

		private final Callback<RequestValue> callback;

		public RequestHandler(Callback<RequestValue> callback) {
			this.callback = callback;
		}

		@Override
		public void onResponseReceived(Request request, Response response) {
			try {
				@SuppressWarnings("unchecked")
				RequestValue value = (RequestValue) JSONParser.parseLenient(response.getText());
				this.callback.onReceive(value);
			} catch (Throwable th) {
				this.callback.onError(th);
			} finally {
				flush();
			}
		}

		@Override
		public void onError(Request request, Throwable exception) {
			this.callback.onError(exception);
			flush();
		}

	}

}
